package com.springboot.frame.starter;

import com.github.benmanes.caffeine.cache.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author liheng
 * @ClassName AbstractCaffineCache
 * @Description
 * @date 2021-01-11 11:27
 */
@Slf4j
class WxCaffineCache<T>  implements WxCacheTemplate<T> {
    /**
     * 缓存环境
     */
    private String env = "wx";

    /**
     * 本地缓存实例
     */
    private LoadingCache<String, Object> loadingCache;

    /**
     * 构造函数
     *
     */
    public WxCaffineCache() {
        WxCache cache = WxCache.options().setTimeUnit(TimeUnit.SECONDS).build();
        // 构建本地缓存实例
        this.loadingCache = caffineCacheManage(cache);
    }


    @Override
    public boolean setKey(String key, T value, long tokenExpireTime) {
        if (Objects.isNull(this.loadingCache)) {
            return Boolean.FALSE;
        }
        if (StringUtils.hasLength(this.env)) {
            this.loadingCache.put(this.env + ":" + key, value);
        } else {
            this.loadingCache.put(key, value);
        }
        return Boolean.TRUE;
    }

    @Override
    public T getKey(String key) {
        if (Objects.isNull(this.loadingCache)) {
            return null;
        }
        try {
            if (StringUtils.hasLength(this.env)) {
                return (T) this.loadingCache.get(this.env + ":" + key);
            } else {
                return (T) this.loadingCache.get(key);
            }
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public boolean delKey(String key) {
        if (Objects.isNull(this.loadingCache)) {
            return Boolean.FALSE;
        }
        if (StringUtils.hasLength(this.env)) {
            this.loadingCache.invalidate(this.env + ":" + key);
        } else {
            this.loadingCache.invalidate(key);
        }
        return Boolean.TRUE;
    }

    /**
     * 缓存管理
     *
     * @param cache
     * @param <T>
     * @return
     */
    private static <T> LoadingCache<String, T> caffineCacheManage(WxCache cache) {
        log.info("初始化缓存的实体数据：{}", cache);
        if (Objects.isNull(cache)) {
            throw new NullPointerException("请实例化一个Cache对象!");
        }
        LoadingCache<String, T> localcache =
                // 构建本地缓存，调用链的方式
                // ,1000是设置缓存的初始化容量，maximumSize是设置缓存最大容量，当超过了最大容量，guava将使用LRU算法（最少使用算法），来移除缓存项
                // expireAfterAccess(12,TimeUnit.HOURS)设置缓存有效期为12个小时
                Caffeine.newBuilder().initialCapacity(cache.getInitialCapacity()).maximumSize(cache.getMaximumSize())
                        // 设置写缓存后n秒钟过期
                        // .expireAfterWrite(30, TimeUnit.SECONDS)
                        .expireAfterWrite(cache.getDuration(), cache.getTimeunit())
                        // 设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
                        //.expireAfterAccess(googleCache.getDuration(), googleCache.getTimeunit())
                        // 只阻塞当前数据加载线程，其他线程返回旧值
                        //.refreshAfterWrite(10, TimeUnit.SECONDS)
                        // 设置缓存的移除通知//用户手动移除EXPLICIT,
                        // //用户手动替换REPLACED,//被垃圾回收COLLECTED,//超时过期EXPIRED,//SIZE由于缓存大小限制
                        .removalListener(new RemovalListener<String, T>() {
                            @Override
                            public void onRemoval(String key, Object value, RemovalCause cause) {
                                log.info(key + ":" + value + ":" + cause.name());
                            }
                        })
                        // build里面要实现一个匿名抽象类
                        .build(new CacheLoader<String, T>() {
                            // 这个方法是默认的数据加载实现,get的时候，如果key没有对应的值，就调用这个方法进行加载。此处是没有默认值则返回null
                            @Override
                            public T load(String key) throws Exception {
                                return null;
                            }
                        });
        return localcache;
    }
}
